using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Windows.Controls.Primitives;
using FluentSilverlight.Commands;
using FluentSilverlight.Interceptors;

namespace FluentSilverlight.Expressions.Buttons
{
    public abstract class ButtonBaseExpression<THIS, TModel, VIEW> : ControlExpression<THIS, TModel, VIEW>
        where THIS : ButtonBaseExpression<THIS, TModel, VIEW>
        where TModel : class
        where VIEW : ButtonBase
    {
        private readonly TModel model;
        private IFsCommand delegateCommand;
        private bool hideIfCannotExecuteCommand = false;

        protected ButtonBaseExpression(TModel model)
        {
            this.model = model;
            DataContext(model);
        }

        protected ButtonBaseExpression(TModel model, string name)
            : base(name)
        {
            this.model = model;
            DataContext(model);
        }

        public THIS OnClick<T>(Expression<Func<TModel, DelegateCommand<T>>> expression)
        {
            var fi = ((MemberExpression)expression.Body).Member as FieldInfo;
            delegateCommand = (IFsCommand)fi.GetValue(model);
            return thisInstance();
        }

        public THIS OnClick<T>(Expression<Func<TModel, DelegateCommand<T>>> expression, bool hideIfCannotExecuteCommand)
        {
            this.hideIfCannotExecuteCommand = hideIfCannotExecuteCommand;
            return OnClick(expression);
        }

        protected override void BuildUp(VIEW element)
        {
            base.BuildUp(element);

            if (delegateCommand == null) return;

            var parameter = GetCommandParameter(element);
            element.Click += (sender, e) => delegateCommand.Execute(parameter);
            delegateCommand.CanExecuteChanged += (sender, e) => { element.IsEnabled = delegateCommand.CanExecute(); };
            // initial setting
            element.IsEnabled = delegateCommand.CanExecute();
        }

        protected override void BindVisibility(VIEW element)
        {
            if (delegateCommand == null) return;

            if (visibleExpression == null)
            {
                delegateCommand.CanExecuteChanged += (sender, e) => SetVisibility(element);
                // initial setting
                SetVisibility(element);
                return;
            }

            var binder = new CommandVisibilityBinder<TModel, VIEW>(element, delegateCommand,
                hideIfCannotExecuteCommand, model, visibleExpression);
        }

        private void SetVisibility(VIEW element)
        {
            var isVisible = delegateCommand.CanExecute() || !hideIfCannotExecuteCommand;
            element.Visibility = isVisible.ToVisibility();
        }
    }
}